#include <stdio.h>      /* perror */
#include <stdlib.h>     /* exit atoi */
#include <sys/types.h>  /* socket           bind listen recvfrom sendto */
#include <sys/socket.h> /* socket inet_addr bind listen recvfrom sendto */
#include <strings.h>    /* bzero */
#include <arpa/inet.h>  /* htons  inet_addr inet_pton */
#include <netinet/in.h> /* ntohs  inet_addr inet_ntop*/
#include <unistd.h>     /* close */
#include <string.h>     /* strlen strcat*/

void usage(char *str); /* 提示信息打印函数 */

int main(int argc, char *argv[])
{
	/* 参数判断及端口号处理 */
	int port = -1;
	if (argc != 3)/* 参数数量不对时打印提示信息 */
	{
		usage(argv[0]);
		exit(-1);
	}
	port = atoi(argv[2]);/* 字符串转数字 */
	if (port < 5000)
	{
		usage(argv[0]);
		exit(-1);
	}
	/* 1.创建套接字，得到socket描述符 */
	int socket_fd = -1; /* 接收服务器端socket描述符 */
	if((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)/* SOCK_STREAM,使用UDP协议 */
	{
		perror ("socket");
		exit(-1);
	}
	/* 允许绑定地址快速重用 */
	int b_reuse = 1;
	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
	/* 2.将套接字与指定端口号和IP进行绑定 */
	/* 2.1填充struct sockaddr_in结构体变量 */
	struct sockaddr_in sin;
	bzero (&sin, sizeof (sin));  /* 将内存块（字符串）的前n个字节清零 */
	sin.sin_family = AF_INET;    /* 协议族, IPv4 */
	sin.sin_port = htons(port);  /* 网络字节序的端口号 */
	if(inet_pton(AF_INET, argv[1], (void *)&sin.sin_addr) != 1)/* 填充IP地址,INADDR_ANY表示允许监听任意IP,但是它其实是(in_addr_t) 0x00000000 */
	{
		perror ("inet_pton");
		exit(-1);
	}
	/* 2.2绑定 */
	if(bind(socket_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		perror("bind");
		exit(-1);
	}
	printf ("Server starting....OK!\n");
	/* 3.数据读写 */
	/* 3.1客户端IP地址及端口号获取的相关变量 */
	struct sockaddr_in cin;          /* 用于存储成功连接的客户端的IP信息 */
	socklen_t addrlen = sizeof(cin);
	char ipv4_addr[16];
	/* 3.2数据读写相关变量 */
	char buf[BUFSIZ]; /* BUFSIZ是在stdio.h中定义的一个宏，值为8192 */
	char replay[BUFSIZ];
	while(1)
	{
		/* 4.接收来自客户端的数据 */
		bzero(buf, BUFSIZ);
		bzero(replay, BUFSIZ);
		if(recvfrom(socket_fd, buf, BUFSIZ-1, 0,(struct sockaddr *)&cin, &addrlen ) < 0)
		{
			perror("recvfrom");
			continue;
		}
		/* 5.打印客户端的IP信息 */
		if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin)))
		{
			perror ("inet_ntop");
			exit(-1);
		}
		printf("Recived from(%s:%d), data:%s", ipv4_addr, ntohs(cin.sin_port), buf);
		/* 6.对客户端做出应答 */
		strcat(replay, buf);
		if(sendto(socket_fd, replay, strlen(replay), 0, (struct sockaddr *)&cin, addrlen) < 0)
		{
			perror("sendto");
			continue;
		}
		/*7.判断是否需要退出 */
		if(!strncasecmp(buf, "quit", strlen("quit")))  	//用户输入了quit字符
		{
			printf ("Client(%s:%d) is exiting!\n", ipv4_addr, ntohs(cin.sin_port));
			// break; /* 防止有一个客户端的退出而导致服务器端退出 */
		}
	}
	/* 8.关闭文件描述符 */
	close(socket_fd);

	return 0;
}

/**
 * @Function: usage
 * @Description: 用户提示信息打印函数
 * @param str : 当前应用程序命令字符串，一般是./app
 * @return  : none
 */
void usage(char *str)
{
	printf ("\n%s serv_ip serv_port", str);
	printf ("\n\t serv_ip: server ip address");
	printf ("\n\t serv_port: server port(>5000)\n\n");
	printf ("\n\t Attention: The IP address must be the IP address of the local nic or 0.0.0.0 \n\n");
}
